#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "mnemonicHandlers.h"
#include "keywordMatch.h"
#include "labelHandlers.h"


int assignByteLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager) {
    getNextString (writeBuffer, pFile);
    int lPos = findLabelByName (lManager, writeBuffer);

    if (lPos != -1) {
        int value;
        int valueLength;
        int valueType;
        int allowTypes;

        getNextString (writeBuffer, pFile);
        allowTypes = INTYPE_ASCII | INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE | INTYPE_LABEL_ARRAY;
        if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) != -1) {
            int temp = 0;
            if (valueType & (INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE | INTYPE_LABEL_ARRAY)) {
                temp = value;
            } else if (valueType & (INTYPE_ASCII)) {
                temp = ((char*)value)[0];
                free((char*)value);
            }
            lManager->labels[lPos].value = temp;
        }
    }
    return 0;
}


int assignArrayLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager) {
    //get array length
    getNextString (writeBuffer, pFile);
    if (!isNumber(writeBuffer)) {
        //error
        return -1;
    }

    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    allowTypes = INTYPE_IMMEDIATE;

    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1) {
        //error
        return -1;
    }
    int arrayLength = value;

    //getName
    getNextString (writeBuffer, pFile);
    int lPos = findLabelByName (lManager, writeBuffer);

    lManager->labels[lPos].array = malloc (sizeof (int) * arrayLength);

    int arrayCounter = 0;
    while (arrayCounter < arrayLength) {
        getNextString (writeBuffer, pFile);
        allowTypes = INTYPE_ASCII | INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE | INTYPE_LABEL_ARRAY;
        if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) != -1) {
            if (valueType & (INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE | INTYPE_LABEL_ARRAY)) {
                lManager->labels[lPos].array[arrayCounter] = value;
                arrayCounter ++;
            } else if (valueType & (INTYPE_ASCII)) {
                int i;
                for (i = 0; i < valueLength; i++) {
                    lManager->labels[lPos].array[arrayCounter] = ((char*)value)[arrayCounter];
                    arrayCounter++;
                }
                free((char*)value);
            }
        } else {
            //error
            return -1;
        }
    }
    lManager->labels[lPos].arrayLen = arrayCounter;
//    int i;
//    for (i = 0; i < arrayLength; i++) {
//        printf ("%d\n", lManager->labels[lPos].array[i]);
//    }
    return 0;
}


int addLabelLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, int value) {
    getNextString (writeBuffer, pFile);
    char * name = malloc(sizeof(char) * MAX_STRING_LEN);
    strcpy(name, writeBuffer);
    addLabel(errorMsg, lManager, LABEL_LABEL, -1, value, -1, (int*)-1, name);
    return 0;
}

int addSpecialLabelLabel (char * errorMsg, FILE * pFile, char * labelName, struct labelManager * lManager, int value) {
    char * name = malloc(sizeof(char) * MAX_STRING_LEN);
    strcpy(name, labelName);
    addLabel(errorMsg, lManager, LABEL_LABEL, -1, value, -1, (int*)-1, name);
    return 0;
}

int addByteLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager) {

    getNextString (writeBuffer, pFile);
    char * name = malloc(sizeof(char) * MAX_STRING_LEN);
    strcpy (name, writeBuffer);
    getNextString (writeBuffer, pFile);
    addLabel(errorMsg, lManager, LABEL_BYTE, -1, -1, -1, (int*)0, name);
    return 0;
}


int addArrayLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager) {
    //get array length
    getNextString (writeBuffer, pFile);
    if (!isNumber(writeBuffer)) {
        //error
        return -1;
    }

    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    allowTypes = INTYPE_IMMEDIATE;

    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1) {
        //error
        return -1;
    }
    int arrayLength = value;

    //getName
    getNextString (writeBuffer, pFile);
    char * name = malloc(sizeof(char) * MAX_STRING_LEN);
    strcpy (name, writeBuffer);

    int arrayCounter = 0;
    while (arrayCounter < arrayLength) {
        getNextString (writeBuffer, pFile);
        allowTypes = INTYPE_ASCII | INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE;
        if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) != -1) {
            if (valueType & (INTYPE_LABEL_LABEL | INTYPE_LABEL_BYTE | INTYPE_IMMEDIATE)) {
                arrayCounter ++;
            } else if (valueType & (INTYPE_ASCII)) {
                arrayCounter += valueLength;
                free((char*)value);
            }
        } else {
            //error
            return -1;
        }
    }
    addLabel(errorMsg, lManager, LABEL_ARRAY, -1, -1, arrayLength, (int*)0, name);

    int lPos = findLabelByName (lManager, name);
//    int i;
//    for (i = 0; i < arrayLength; i++) {
//        printf ("%d\n", lManager->labels[lPos].array[i]);
//    }
    return 0;
}

int addImmediateLabel (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager) {
    int value;
    int valueType;
    int valueLength;
    int allowTypes;

    //getNextString (writeBuffer, pFile);
    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_IMMEDIATE | INTYPE_ASCII;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) != -1) {
        int temp = 0;
        if (valueType & (INTYPE_IMMEDIATE)) {
            temp = value;
        } else if (valueType & (INTYPE_ASCII)) {
            temp = ((char*)value)[0];
            free((char*)value);
        }
        addLabel(errorMsg, lManager, LABEL_IMMEDIATE_LABEL, -1, temp, -1, (int*)0, (char*)0);
    }
    return 0;
}

int findLabelByName (struct labelManager * lManager, char * name) {
    int nLabels = lManager->currentLabel;
    int i;
    for (i = 0; i < nLabels; i++) {
        if (lManager->labels[i].type != LABEL_IMMEDIATE_LABEL) {
            if (!strcmp(lManager->labels[i].name, name)) {
                return i;
            }
        }
    }
    return -1;
}


int findImmediateLabelByValue (struct labelManager * lManager, int value)
{
    int nLabels = lManager->currentLabel;
    int i;
    for (i = 0; i < nLabels; i++) {
        if (lManager->labels[i].type == LABEL_IMMEDIATE_LABEL && lManager->labels[i].value == value) {
            return i;
        }
    }
    return -1;
}
